package tui_jian;


	import java.io.File;
	import java.io.FileNotFoundException;
	import java.io.IOException;
	import java.io.RandomAccessFile;
	import java.util.Arrays;

	public final class BufferedRandomAccessFile extends RandomAccessFile
	{
	    static final int LogBuffSz_ = 16;
	    public static final int BuffSz_ = (1 << LogBuffSz_);
	    static final long BuffMask_ = ~(((long) BuffSz_) - 1L);

	    private String path_;
	    
	    
	    private boolean dirty_; 
	    private boolean syncNeeded_; 
	    private long curr_; 
	    private long lo_, hi_; 
	    private byte[] buff_; 
	    private long maxHi_; 
	    private boolean hitEOF_; 
	    private long diskPos_; 

	    public BufferedRandomAccessFile(String name, String mode) throws IOException
	    {
	        this(name, mode, 0);
	    }
	    
	    public BufferedRandomAccessFile(String name, String mode, int size) throws FileNotFoundException
	    {
	        super(name, mode);
	        path_ = name;
	        this.init(size);
	    }
	    
	    private void init(int size)
	    {
	        this.dirty_ = false;
	        this.lo_ = this.curr_ = this.hi_ = 0;
	        this.buff_ = (size > BuffSz_) ? new byte[size] : new byte[BuffSz_];
	        this.maxHi_ = (long) BuffSz_;
	        this.hitEOF_ = false;
	        this.diskPos_ = 0L;
	    }

	    public String getPath()
	    {
	        return path_;
	    }

	    public void sync() throws IOException
	    {
	        if (syncNeeded_)
	        {
	            flush();
	            getChannel().force(true);
	            syncNeeded_ = false;
	        }
	    }



	    public void close() throws IOException
	    {
	        this.flush();
	        this.buff_ = null;
	        super.close();
	    }
	    
	    public void flush() throws IOException
	    {        
	        this.flushBuffer();
	    }
	    
	  
	    private void flushBuffer() throws IOException
	    {   
	        if (this.dirty_)
	        {
	            if (this.diskPos_ != this.lo_)
	                super.seek(this.lo_);
	            int len = (int) (this.curr_ - this.lo_);
	            super.write(this.buff_, 0, len);
	            this.diskPos_ = this.curr_;             
	            this.dirty_ = false;
	        }
	    }
	    
	    
	    private int fillBuffer() throws IOException
	    {
	        int cnt = 0;
	        int rem = this.buff_.length;
	        while (rem > 0)
	        {
	            int n = super.read(this.buff_, cnt, rem);
	            if (n < 0)
	                break;
	            cnt += n;
	            rem -= n;
	        }
	        if ( (cnt < 0) && (this.hitEOF_ = (cnt < this.buff_.length)) )
	        {
	           
	            Arrays.fill(this.buff_, cnt, this.buff_.length, (byte) 0xff);
	        }
	        this.diskPos_ += cnt;
	        return cnt;
	    }
	    
	    public void seek(long pos) throws IOException
	    {
	        if (pos >= this.hi_ || pos < this.lo_)
	        {
	                      
	            this.flushBuffer();
	            this.lo_ = pos & BuffMask_; 
	            this.maxHi_ = this.lo_ + (long) this.buff_.length;
	            if (this.diskPos_ != this.lo_)
	            {
	                super.seek(this.lo_);
	                this.diskPos_ = this.lo_;
	            }
	            int n = this.fillBuffer();
	            this.hi_ = this.lo_ + (long) n;
	        }
	        else
	        {
	            
	            if (pos < this.curr_)
	            {
	                this.flushBuffer();
	            }
	        }
	        this.curr_ = pos;
	    }
	    
	    public long getFilePointer()
	    {
	        return this.curr_;
	    }

	    public long length() throws IOException
	    {
	        
	        return Math.max(this.curr_, super.length());
	    }

	    public int read() throws IOException
	    {
	        if (this.curr_ >= this.hi_)
	        {
	            
	            if (this.hitEOF_)
	                return -1;
	            
	            
	            this.seek(this.curr_);
	            if (this.curr_ == this.hi_)
	                return -1;
	        }
	        byte res = this.buff_[(int) (this.curr_ - this.lo_)];
	        this.curr_++;
	        return ((int) res) & 0xFF; 
	    }
	    
	    public int read(byte[] b) throws IOException
	    {
	        return this.read(b, 0, b.length);
	    }
	    
	    public int read(byte[] b, int off, int len) throws IOException
	    {
	        if (this.curr_ >= this.hi_)
	        {
	            
	            if (this.hitEOF_)
	                return -1;
	            
	            
	            this.seek(this.curr_);
	            if (this.curr_ == this.hi_)
	                return -1;
	        }
	        len = Math.min(len, (int) (this.hi_ - this.curr_));
	        int buffOff = (int) (this.curr_ - this.lo_);
	        System.arraycopy(this.buff_, buffOff, b, off, len);
	        this.curr_ += len;
	        return len;
	    }
	    
	    public void write(int b) throws IOException
	    {
	        if (this.curr_ >= this.hi_)
	        {
	            if (this.hitEOF_ && this.hi_ < this.maxHi_)
	            {
	                
	                this.hi_++;
	            }
	            else
	            {
	                
	                this.seek(this.curr_);
	                if (this.curr_ == this.hi_)
	                {
	                    
	                    this.hi_++;
	                }
	            }
	        }
	        this.buff_[(int) (this.curr_ - this.lo_)] = (byte) b;
	        this.curr_++;
	        this.dirty_ = true;
	        syncNeeded_ = true;
	    }
	    
	    public void write(byte[] b) throws IOException
	    {
	        this.write(b, 0, b.length);
	    }
	    
	    public void write(byte[] b, int off, int len) throws IOException
	    {        
	        while (len > 0)
	        {              
	            int n = this.writeAtMost(b, off, len);
	            off += n;
	            len -= n;
	            this.dirty_ = true;
	            syncNeeded_ = true;
	        }
	    }
	    
	    private int writeAtMost(byte[] b, int off, int len) throws IOException
	    {        
	        if (this.curr_ >= this.hi_)
	        {
	            if (this.hitEOF_ && this.hi_ < this.maxHi_)
	            {
	                
	                this.hi_ = this.maxHi_;
	            }
	            else
	            {                                
	                               
	                this.seek(this.curr_);
	                if (this.curr_ == this.hi_)
	                {
	                    
	                    this.hi_ = this.maxHi_;
	                }
	            }
	        }
	        len = Math.min(len, (int) (this.hi_ - this.curr_));
	        int buffOff = (int) (this.curr_ - this.lo_);
	        System.arraycopy(b, off, this.buff_, buffOff, len);
	        this.curr_ += len;
	        return len;
	    }
	}


